Второе издание замечательной книги The iPhone Developer's Cookbook Автор Эрика Садун.
Обязательно к прочтению. PDF 12 мб
The iPhone Developer's Cookbook: Building Applications with the iPhone 3.0 SDK
Этот блог о языке Objective-C и о программировании для iPhone Различные полезные мелочи и интересные решения. Если будете что-то копировать отсюда - не забудьте поставить ссылку на этот блог.
Второе издание замечательной книги The iPhone Developer's Cookbook Автор Эрика Садун.
Обязательно к прочтению. PDF 12 мб
The iPhone Developer's Cookbook: Building Applications with the iPhone 3.0 SDK
Содержимое UIscrollView может двигаться по инерции, если его резко "запустить" пальцем. Но что, если это движение надо резко остановить? Делается это так:
[scrollView scrollRectToVisible:CGRectMake(scrollView.contentOffset.x, scrollView.contentOffset.y + 0.01, scrollView.frame.size.width, scrollView.frame.size.height) animated:NO];
Все
Довольно часто нужно знать и контролировать те данные, что пользователь вводит в текстовые поля. Причем контролировать непосредственно в процессе ввода. Это вполне возможно.
И UITextView и UITextField шлют системе уведомления о изменении своего состояния (UITextViewTextDidChangeNotification и UITextFieldTextDidChangeNotification ). Шлют они их, используя NSNotificationCenter. Все что нам нужно, это слушать этот NotificationCenter и реагировать на нужные нам уведомления.
Пара слов о работе с NSNotificationCenter
NSNotificationCenter *notCenter = [NSNotificationCenter defaultCenter];
Это ссылка на NotificationCenter, через который по умолчанию ходят все системные сообщения (в том числе и нужные нам)
[[NSNotificationCenter defaultCenter]postNotificationName: @"NAME_OF_NOTIFICATION" object: self];
Так можно отправлять уведомления. Как видно, уведомление имеет имя и ссылку на объект (обычно это объект отправивший уведомлене). То есть мы можем видеть кто и какое уведомление отправил. В нашем случае мы будем только ловить уведомления, отправленные текстовыми полями
[[NSNotificationCenter defaultCenter] addObserver:self selector: @selector(meth:) name:@"UITextViewTextDidChangeNotification" object: nil];
Регистрируем слушателя уведомлений. Рассмотрим все 4 параметра
addObserver: Объект, который будет реагировать на уведомления
selector: селектор (метод) объекта, который будет вызван при приходе уведомления
name: строка-имя уведомления, которое мы ждем
object: объект, уведомления от которого мы ждем. Если nil, то принимаем уведомления от любого объекта.
- (void)meth:(NSNotification*)notification;
Так выглядит метод(selector:), который будет вызван при приходе уведомления
Ну и наконец-то возвращаемся к теме статьи. Небольшой пример. Класс, который будет менять буквы, вводимые в любом текстовом поле приложения на ПРОПИСНЫЕ
-(id) init {
if(self = [super init]) {
[[NSNotificationCenter defaultCenter] addObserver:self selector: @selector(capitalizeText:) name:@"UITextFieldTextDidChangeNotification" object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector: @selector(capitalizeText:) name:@"UITextViewTextDidChangeNotification" object:nil];
}
return self;
}
-(void) capitalizeText: (NSNotification*)notification{
[[NSNotificationCenter defaultCenter]removeObserver: self name: [notification name] object: nil];
UIView *textItem = [notification object]; // textField or textView
NSString *text = [textItem text];
text = [text uppercaseString];
[textItem setText: text];
[[NSNotificationCenter defaultCenter] addObserver:self selector: @selector(capitalizeText:) name:[notification name] object:nil];
}
- (void) dealloc
{
[[NSNotificationCenter defaultCenter]removeObserver: self name: @"UITextFieldTextDidChangeNotification" object: nil];
[[NSNotificationCenter defaultCenter]removeObserver: self name: @"UITextViewTextDidChangeNotification" object: nil];
[super dealloc];
}
Обратите внимание на то, что уведомление xxxTextDidChangeNotification будет слаться и при программном изменении текста в текстовом поле. поэтому мы в методе capitalizeText: временно отключаам наблюдение.
Все. Будут вопросы-предложения-комментарии - пишите ))
Очень часто (особенно при отладке) необходимо выводить в консоль CGPoint, CGRect и CGSize. Чтоб не писать громоздкие конструкции, воспользуйтесь функциями
NSStringFromCGPoint(CGPoint point);
NSStringFromCGRect(CGRect rect);
NSStringFromCGSize(CGSize size);
CGRect rect = CGRectMake(1, 2, 3, 4);
NSString *s = NSStringFromCGRect(rect);
NSLog(s);
Так же есть и обратные функции CGPointFromString, CGRectFromString, и CGSizeFromString
Иногда хочется создать вью в интерфейс билдере, а потом в процессе работы загрузить и показать его. Делается это просто:
1. Создаем в проекте класс - наследник UIView (например myView)
2. Создаем в IB вью, кладем на него все что пожелаем и в инспекторе на последней закладке указываем имя нашего класса (myView)
3. Сохраняем и подключаем к проекту (например с именем myViev.xib)
4. Теперь чтобы в проекте вызвать созданый вью пишем:
myView *view = [[[NSBundle mainBundle] loadNibNamed:@"myViev" owner: self options:nil] objectAtIndex:0];
Все можно пользоваться этим вью как угодно )))
Получить страну-область по координатам
http://maps.google.com/maps/geo?ll=46.3,30.46&output=json&hl=en
Выводим имя файла, номер строки, имя класса и метода в консоль
NSLog(@"%s === %d === %s", __FILE__, __LINE__, __PRETTY_FUNCTION__, __FUNCTION__);
Каждое приложение имеет свою иконку на рабочем столе iPhon-a
Чтобы установить эту картинку, надо в файле info.plist прописать имя картинки для параметра Icon file
Чтобы убрать с иконки уродливую полукруглую тень, надо в файле info.plist добавить параметр UIPrerenderedIcon типа boolean и установить его значение в YES
Если задержать палец на гиперссылке в UIWebView, то через пару секунд вылезет окошко с предложением скопировать ссылку или перейти по ней (OS 3.0 и выше). Чтобы запретить это окошко выполняем следующий javaScript
NSString *jsString =@"document.documentElement.style.webkitTouchCallout=\"none\"";
[webView stringByEvaluatingJavaScriptFromString: jsString];
Выполнять конечно надо после того, как webView закончит загрузку
Если нужно сохранить небольшое количество данных, и прочитать их при следующем запуске приложения, то на помощь приходит класс NSUserDefaults
Сохранение данных:
NSString* str = @"StringToSave";
NSArray *arr = [[NSArray alloc]initWithObjects: @"array", @"to", @"save",nil];
[[NSUserDefaults standardUserDefaults] setObject: str forKey:@"stringSetting"];
[[NSUserDefaults standardUserDefaults] setObject: arr forKey:@"this_is_array"];
[[NSUserDefaults standardUserDefaults] synchronize];
Чтение данных:
NSString* str = [[NSUserDefaults standardUserDefaults] objectForKey:@"stringSetting"];
NSArray *a = [[NSUserDefaults standardUserDefaults] objectForKey:@"this_is_array"];
Сохранять данные лучше всего при выходе из приложения - в методе - (void)applicationWillTerminate:(UIApplication *)application вашего делегата UIApplicationDelegate
Для того, чтобы сделать анимированое изображение, надо иметь набор изображений, которые будут последовательно сменять друг друга.
Добавляем эти файлы в проект и делаем из них анимированое изображение:
NSArray *imagesList = [NSArray arrayWithObjects:
[UIImage imageNamed:@"image1.png"],
[UIImage imageNamed:@"image2.png"],
[UIImage imageNamed:@"image3.png"],
[UIImage imageNamed:@"image4.gif"],
nil];
UIImageView *animatedView = [[UIImageView alloc]initWithFrame: [self frame]];
animatedView.animationImages = imagesList;
animatedView.animationDuration = 0.25; // seconds
animatedView.animationRepeatCount = 0; // 0 = forever
[animatedView startAnimating];
[self addSubview:animatedView];
[animatedView release];
Если нужно получить текущее положение скролла в UIWebView, либо устоновить скролл в заданную позицию, то есть 2 пути - Использовать java script или недокументированые методы. Как правило использование законных путей лучше. Поэтому о них и поговорим
Получить текущее положение скролла (вертикальное)
int scrollPosition = [[webView stringByEvaluatingJavaScriptFromString:@"window.pageYOffset"] intValue];
Установить вертикальное положение скролла
int yOffset = 123;
NSString *jsString = [NSString stringWithFormat: @"window.scrollTo(0, %d) ", yOffset];
[_webView stringByEvaluatingJavaScriptFromString: jsString];
Для того, чтобы во время работы уменьшить/увеличить размер шрифта в UIWebView, воспользуемся архиполезной возможностью этого элемента, выполнять java script
int fontScale = 150;
NSString *jsString = [NSString stringWithFormat: @"document.getElementsByTagName('body')[0].style.webkitTextSizeAdjust= '%d%%'", fontScale];
[_webView stringByEvaluatingJavaScriptFromString: jsString];
Конечно делать это нужно только после того, как UIWebView закончит загрузку своего содержимого
Иногда надо контролировать клики пользователя по гиперссылкам в UIWebView для этого необходимо назначить для webView делегата и переопределить в нем метод - (BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType
Приведенный ниже код контролирует клики на линки и не дает пользователю переходить по ссылкам, заканчивающимся на ".com"
- (BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType
{
if(navigationType == UIWebViewNavigationTypeLinkClicked)
{
NSString *fullLink = request.URL.path;
if([fullLink hasSuffix:@".com"])
{return NO;}
}
return YES;
}
Иногда нужно создать свой форматированый HTML текст и вывести его на экран. Для этого используется элемент UIWebView Если же в этом тексте должны быть картинки, то
1. Добавляем файлы с картинками в проект
2. В HTML пишем просто
3. В коде пишем
NSString *htmlString = @"Html with image ";
NSString *path = [[NSBundle mainBundle] bundlePath];
NSURL *baseURL = [NSURL fileURLWithPath: path];
[webView loadHTMLString: htmlString baseURL: baseURL];
Этот код делает "фотографию экрана и помещает ее в галлерею iPhone
CGRect rect = [[UIScreen mainScreen] bounds];
UIGraphicsBeginImageContext(rect.size);
[currentViewController.view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIImageWriteToSavedPhotosAlbum(viewImage, nil, nil, nil);
Иногда нужно получить случайное число. Для этого в objective-C есть 2 функции rand() и random() Однако они каждый раз при запуске будут выдавать одну и ту же последовательность псевдо-случайных чисел.
Поэтому если нужна действительно случайная последовательность, то можно привязываться к текущей дате
Этот код выдаёт случайное число в диапазоне (startVal : endVal) - не включая endVal.
srandom((unsigned)(mach_absolute_time() & 0xFFFFFFFF));
int randomValue = startVal+ (random() % (endVal - startVal));
Как вам наверняка известно, Objective-C позволяет быстро создавать сеттеры и геттеры с помощью директив @property и @synthesize
Однако главное на мой взгляд достоинство этих @property в возможностях, представляемых механизмом Key-Value-Mechanisms Об этих возможностях чуть поподробнее:
Имеем класс
@interface ClassWithProperties
...
@property int integerProperty;
@property id idProperty;
@property NSString *strProperty;
1. Доступ к свойствам по их имени во время выполнения программы. Имя свойства может быть неизвестно на этапе компиляции и вычисляться только после старта. Обратите внимание, что метод работает только с наследниками NSObject И поэтому примитивные типы должны быть преобразованы в NSValue и NSNumber Обратное преобразование механизм property делает сам автоматически
ClassWithProperties *cl;
[cl setValue: [NSNumber numberWithInt: 10] forKey: @"integerProperty"];
[cl setValue: self forKey: @"idProperty"];
[cl setValue: @"just a string" forKey: @"strProperty"];
......
int i = [[cl valueForKey: @"integerProperty"] intValue];
NSString *s = [cl valueForKey: @"strProperty"];
2. Иногда возникает необходимость формировать свойства на этапе выполнения. Чтоб пользователь мог обратиться к
[cl setValue: @"someValue" forKey: @"AnyNameAsYouWant"]; и [cl valueForKey: @"AnyNameAsYouWant"];
даже если на этапе компиляции это имя "AnyNameAsYouWant" неизвестно. Тут на помощь приходят методы NSObject-а (void)setValue:(id)value forUndefinedKey:(NSString *)key и (id)valueForUndefinedKey:(NSString *)key переопределив эти методы мы получим возможность сохранять и полученные значения например в NSMutableDictionary.
3. Получение имен всех свойств. Если мы хотим получить значения всех property класса (например чтобы их сохранить при выходе), то нам нужно сперва получить все имена, а потом через valueForKey можно будет вытащить и значения. Имена всех свойств класса получаются так:
unsigned int outCount;
objc_property_t *properties = class_copyPropertyList([AnyClassThatYouWant class], &outCount);
for(int i = 0; i < outCount; i++)
{
objc_property_t property = properties[i];
const char *propName = property_getName(property);
if(propName)
{
NSString *name = [NSString stringWithUTF8String:propName];
NSLog(name);
}
}
free(properties);
Иногда необходимо создать метод, который будет принимать неизвестное число однотипных аргументов (Как например [NSString stringWithFormat]).
Сделать это несложно.
Объявление метода:
-(void) multiArgsMeth: (int)agr0, ...;
Реализация метода:
-(void) multiArgsMeth: (id)agr0, ...{
va_list argumentList;
va_start(argumentList, arg0); // Start scanning for arguments after firstObject.
while (parType = va_arg(argumentList, id)) // As many times as we can get an argument of type "id"
{
//DO what yo want
}
va_end(argumentList);
}
Вызов метода (не забудьте про nil в конце):
[self multiArgsMeth : myArg0 , myArg1, myArg2 , nil];